use std::{net::TcpListener, sync::mpsc::{Receiver, Sender}};
use std::net::TcpStream;
use std::io::prelude::*;
use std::fs;
use std::vec::Vec;
use std::time::Duration;
use std::thread;
use std::thread::JoinHandle;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;

fn main() {
    let tcpListener = TcpListener::bind("127.0.0.1:7878").unwrap();
    let thread_pool = ThreadPool::new(2);
    for stream in tcpListener.incoming().take(2) {
        let mut stream = stream.unwrap();
        println!("Connection established");
        thread_pool.execute(move || {
            handle_connection(&mut stream);
        });
    }

}

fn handle_connection(stream:&mut TcpStream) {
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();
    //println!("request {}:", String::from_utf8_lossy(&buffer));
    let get = b"GET / HTTP/1.1\r\n";
    let sleep = b"GET /sleep HTTP/1.1\r\n";
    let (code, file) = if buffer.starts_with(get) {
        ("HTTP/1.1 200 OK\r\n\r\n", "hello.html")
    } else if buffer.starts_with(sleep){
        thread::sleep(Duration::from_secs(5));
        ("HTTP/1.1 200 OK\r\n\r\n", "hello.html")
    } 
    else {
        ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html")
    };
    let file_str = fs::read_to_string(file).unwrap();
    let response = format!("{}{}", code, file_str);
    stream.write(response.as_bytes()).unwrap();
    stream.flush().unwrap();   
    //println!("response \r\n: {}", response);
}

enum Message {
    Stop,
    JobMessage(Job)
}

struct Work {
    id : usize,
    join: Option<JoinHandle<()>>
}

pub struct ThreadPool {
    works: Vec<Work>,
    sender: Sender<Message>
}


type Job = Box<dyn FnOnce() + Send + 'static>;
impl Work {
    pub fn new(id: usize, receiver: Arc<Mutex<Receiver<Message>>>) -> Work {
        let join = thread::spawn(move || {
            loop {
               let message = receiver.lock().unwrap().recv().unwrap();
               match message {
                   Message::JobMessage(job) => {
                       job();
                   },
                   Message::Stop => break
               }
            }
        });
        Work {
            id,
            join: Some(join)
        }
    }
}

impl ThreadPool {
    pub fn new(size: usize) -> ThreadPool {
        assert!(size > 0);
        let mut works = Vec::with_capacity(size);
        let (tx, rx): (Sender<Message>, Receiver<Message>) = mpsc::channel();
        let arc = Arc::new(Mutex::new(rx));
        for id in 0 .. size {
            works.push(Work::new(id, Arc::clone(&arc)));
        }
        ThreadPool {
            works,
            sender: tx
        }
    }

    pub fn execute<F>(&self, f: F)  
        where F: FnOnce() + Send + 'static{
        self.sender.send(Message::JobMessage(Box::new(f))).unwrap();
    }
}

impl Drop for ThreadPool {

    fn drop(&mut self) {
        for _ in self.works.iter() {
            self.sender.send(Message::Stop).unwrap();
        }

        for work in self.works.iter_mut() {
           if let Some(join) = work.join.take() {
               join.join().unwrap();
           }
        }
    }
}

